home *** CD-ROM | disk | FTP | other *** search
- #include <Errors.h>
- #include <FixMath.h>
- #include <LowMem.h>
- #include <Memory.h>
- #include <OSUtils.h>
- #include <Quickdraw.h>
-
- #include "ImageCodec.h"
-
- #ifdef POWERPC_NATIVE
- #pragma options align=mac68k
- #endif
-
- /* Version information */
-
- #define EXAMPLE_CODEC_REV 2
- #define codecInterfaceVersion 2 /* high word returned in component GetVersion */
-
- /*
- Our data structure declarations
- */
-
-
- #define QUEUE_SIZE 30
-
- #ifndef fieldOffset
- #define fieldOffset(type, field) ((short) &((type *) 0)->field)
- #endif
-
- /*
- The DecompressRecord is used to store the information needed
- to decompress a frame asynchronously.
- */
- struct DecompressRecord {
- void *next; // next DecompressRecord that is queued up
- void *nextFree; // next unused DecompressRecord
- ICMCompletionProcRecord completionProc; // completion proc record to call when done
- TimeValue frameTime; // what time to decompress this frame
- Fixed rate; // rate of movie
- long scale; // time scale
- struct Globals *glob; // pointer to our globals
- Boolean inUse; // set while this entry is being used
- };
- typedef struct DecompressRecord DecompressRecord;
-
- /* This is the structure we use to store our global data for each instance */
-
- typedef struct {
- QTCallBack callBack; // our call back
- QTCallBackUPP decompressCallBackUPP; // pointer to our decompress callback
- ImageSequence sequenceID;
- QHdr head; // queue header
- DecompressRecord *firstFreeEntry; // first unused entry
- DecompressRecord queue[QUEUE_SIZE]; // for queued frames
- long a5World; // A5 world for decompress callback
- Boolean queueIsRunning; // set if decompress queue is currently running
- } Globals;
-
-
- /* Function prototypes to keep the compiler smiling. */
-
- pascal ComponentResult
- RAWFAKEREXAMPLECODEC(ComponentParameters *params,char **storage);
-
- pascal ComponentResult
- CDOpen(ComponentInstance self);
-
- pascal ComponentResult
- CDClose(Handle storage,ComponentInstance self);
-
- pascal ComponentResult
- CanDoSelector(short selector);
-
- pascal ComponentResult
- GetVersion(void);
-
- pascal void
- DecompressCallBack(QTCallBack cb,long refcon);
-
- pascal long
- CDCodecFlush(Handle storage);
-
- #ifdef POWERPC_NATIVE
-
- /*
- create a RoutineDescriptor for the Component dispatch routine
- */
- RoutineDescriptor Dispatcher = BUILD_ROUTINE_DESCRIPTOR(kPascalStackBased |
- RESULT_SIZE(kFourByteCode) |
- STACK_ROUTINE_PARAMETER(1,kFourByteCode) |
- STACK_ROUTINE_PARAMETER(2,kFourByteCode),
- RAWFAKEREXAMPLECODEC);
-
- RoutineDescriptor decompressCallBackUPP = BUILD_ROUTINE_DESCRIPTOR(uppQTCallBackProcInfo, DecompressCallBack);
- #endif
-
- /************************************************************************************
- * This is the main dispatcher for our codec. All calls from the codec manager
- * will come through here, with a unique selector and corresponding parameter block.
- *
- * This routine must be first in the code segment of the codec thing.
- */
-
- pascal ComponentResult
- RAWFAKEREXAMPLECODEC(ComponentParameters *params,char **storage)
- {
- switch ( params->what ) {
-
- case kComponentOpenSelect:
- {
- ComponentCallOpenParameters *p = (ComponentCallOpenParameters *)params;
- return CDOpen(p->self);
- }
-
- case kComponentCloseSelect:
- {
- ComponentCallCloseParameters *p = (ComponentCallCloseParameters *)params;
- return CDClose(storage,p->self);
- }
-
- case kComponentCanDoSelect:
- {
- ComponentCallCanDoParameters *p = (ComponentCallCanDoParameters *)params;
- return CanDoSelector(p->selector);
- }
- case kComponentVersionSelect:
- return GetVersion();
-
- case codecGetCodecInfo:
- {
- CodecCallCDGetCodecInfoParameters *p = (CodecCallCDGetCodecInfoParameters *)params;
- return CDGetCodecInfo(storage,p->info);
- }
-
- case codecPreDecompress :
- {
- CodecCallCDPreDecompressParameters *p = (CodecCallCDPreDecompressParameters *)params;
- return CDPreDecompress(storage,p->params);
- }
-
- case codecBandDecompress :
- {
- CodecCallCDBandDecompressParameters *p = (CodecCallCDBandDecompressParameters *)params;
- return CDBandDecompress(storage,p->params);
- }
-
- case codecCDSequenceFlush :
- return CDCodecFlush(storage);
-
- case codecCDSequenceBusy :
- return 0;
-
- case codecNewImageBufferMemory:
- {
- CodecCallCDCodecNewImageBufferMemory *p = (CodecCallCDCodecNewImageBufferMemory *)params;
- return CDCodecNewImageBufferMemory(storage, p->params, p->flags, p->memoryGoneProc, p->refCon);
- }
-
- default:
- return badComponentSelector;
- }
- }
-
- /************************************************************************************
- * This gets called when the component instance is opened. We allocate our storage at this
- * point. If we have shared globals, we check if they exist, and put a pointer to them
- * in our instance globals so that other calls can get to them.
- */
-
- pascal ComponentResult
- CDOpen(ComponentInstance self)
- {
- ComponentResult result = noErr;
- Globals *glob;
-
- /*
- First we allocate our local storage. This should store any
- kind of data used by the thing instance. It should be allocated
- in the current heap.
- */
-
- if ( (glob = (Globals *)NewPtrClear(sizeof(Globals))) == nil ) {
- return MemError();
- }
- SetComponentInstanceStorage(self,(Handle)glob);
-
- #ifdef POWERPC_NATIVE
- glob->decompressCallBackUPP = &decompressCallBackUPP;
- #else
- glob->decompressCallBackUPP = DecompressCallBack;
- #endif
-
- {
- short i;
- for (i = 0; i < (QUEUE_SIZE - 1); i++)
- glob->queue[i].nextFree = &(glob->queue[i + 1]);
- glob->firstFreeEntry = &(glob->queue[0]);
- }
-
- bail:
- return result;
- }
-
- /************************************************************************************
- * This gets called when the thing instance is closed. We need to get rid of any
- * instance storage here.
- */
-
- pascal ComponentResult
- CDClose(Handle storage,ComponentInstance self)
- {
- Globals *glob = (Globals *)storage;
-
- if (glob)
- DisposePtr((Ptr)glob);
-
- return noErr;
- }
-
-
-
- /************************************************************************************
- * Return true if we can handle the selector, otherwise false.
- */
-
- pascal ComponentResult
- CanDoSelector(short selector)
- {
- switch(selector) {
- case kComponentOpenSelect:
- case kComponentCloseSelect:
- case kComponentCanDoSelect:
- case kComponentVersionSelect:
- case codecGetCodecInfo:
- case codecPreDecompress:
- case codecBandDecompress:
- case codecCDSequenceFlush:
- case codecCDSequenceBusy:
- case codecNewImageBufferMemory:
- return true;
- default:
- return false;
- }
- }
-
-
- /************************************************************************************
- * Return the version of this component ( defines interface ) and revision level
- * of the code.
- */
-
- pascal ComponentResult
- GetVersion(void)
- {
- return (codecInterfaceVersion<<16) | EXAMPLE_CODEC_REV; /* interface version in hi word, code rev in lo word */
- }
-
- /*
- CDCodecFlush is called when the image compression manager wants the
- codec to empty its schedule queue. An example would be when a movie is
- playing and the user moves the thumb. The sudden jump in time renders
- any previously scheduled frames useless. So we need to flush the queue so
- we can start over.
- */
- pascal ComponentResult
- CDCodecFlush(Handle storage)
- {
- Globals *glob = (Globals *)storage;
-
- /*
- if there's not a callback proc allocated, we sure don't have any frames
- queued up.
- */
- if (glob->callBack) {
- DecompressRecord *drp;
- long saveA5 = SetA5(glob->a5World);
-
- glob->queueIsRunning = false; // flag queue as not running.
- // otherwise, we'll never start it up again
-
- // kill the callback
- CancelCallBack(glob->callBack);
-
- // tear down the queue
- while (drp = (DecompressRecord *)glob->head.qHead) {
- drp = (DecompressRecord *)(((long)drp) - fieldOffset(DecompressRecord, next));
- if (drp->inUse) {
- // call back to say we're done
- ICMDecompressComplete(glob->sequenceID, -1, codecCompletionSource | codecCompletionDest, &drp->completionProc);
- drp->nextFree = glob->firstFreeEntry;
- glob->firstFreeEntry = drp;
- drp->inUse = false;
- }
- Dequeue(glob->head.qHead, &glob->head);
- }
-
- SetA5(saveA5);
- }
-
- return noErr;
- }
-
- /************************************************************************************
- * CDPreDecompress gets called before an image is decompressed. We return information about
- * how we can decompress the image to the codec manager, so that it can fit the destination data
- * to our requirements.
- */
-
- pascal ComponentResult
- CDPreDecompress(Handle storage,register CodecDecompressParams *p)
- {
- Globals *glob = (Globals *)storage;
- register CodecCapabilities *capabilities = p->capabilities;
-
- /* check to see if this base address is on our screen's GDevice */
- {
- GDHandle gd = LMGetMainDevice();
- Rect bounds = (**gd).gdRect;
- Ptr mainScreenBaseStart, mainScreenBaseEnd;
-
- mainScreenBaseStart = (**(**gd).gdPMap).baseAddr;
- mainScreenBaseEnd = (**(**gd).gdPMap).baseAddr;
- mainScreenBaseEnd += (bounds.bottom - bounds.top) * ((**(**gd).gdPMap).rowBytes & 0x3fff);
-
- if ((mainScreenBaseStart < p->dstPixMap.baseAddr) ||
- (p->dstPixMap.baseAddr >= mainScreenBaseEnd)) {
- return codecConditionErr;
- }
- }
-
- // only allow 16 bpp source
- if ((**p->imageDescription).depth != 16)
- return codecConditionErr;
-
- /* we only support 16 bits per pixel dest */
- if (p->dstPixMap.pixelSize != 16)
- return codecConditionErr;
-
- capabilities->wantedPixelSize = p->dstPixMap.pixelSize;
-
- /* The smallest possible band we can do is 1 scan lines. */
-
- capabilities->bandMin = 1;
-
- /* We can deal with 1 scan line high bands. */
-
- capabilities->bandInc = 1;
-
- /* If we needed our pixels to be aligned on some integer multiple we would set these to
- * the number of pixels we need the dest extended by. If we dont care, or we take care of
- * it ourselves we set them to zero.
- */
-
- capabilities->extendWidth = 0;
- capabilities->extendHeight = 0;
-
- capabilities->flags = codecCanAsyncWhen | codecCanAsync |
- codecCanScale | codecCanMask | codecCanRemapColor | codecCanFastDither;
-
-
- capabilities->flags |= codecImageBufferIsOnScreen;
-
- glob->sequenceID = p->sequenceID;
-
- return noErr;
- }
-
- /*
- This is the call back proc that is used for scheduled asynchronously displayed
- frames. It is called at the scheduled frame time, and should display that
- frame. If there is a frame queued up after this one, it should be scheduled.
- */
- pascal void DecompressCallBack(QTCallBack cb,long refcon)
- {
- DecompressRecord *drp = (DecompressRecord *)refcon;
- Globals *glob = (Globals *)drp->glob;
-
- Dequeue((void *)(&drp->next), &glob->head); // remove this frame from the queue
-
- if (drp->inUse) {
- /* Be sure to call ICMDecompressComplete when done. */
- ICMDecompressComplete(glob->sequenceID, noErr, codecCompletionSource | codecCompletionDest, &drp->completionProc);
- drp->inUse = false; // we're done with this frame
-
- drp->nextFree = glob->firstFreeEntry;
- glob->firstFreeEntry = drp;
-
- // queue up the next one
- if (glob->queueIsRunning && (drp = (void *)glob->head.qHead)) {
- /* there is another frame scheduled. set up the callback task. */
- drp = (DecompressRecord *)(((long)drp) - fieldOffset(DecompressRecord, next));
- CallMeWhen(
- glob->callBack,
- glob->decompressCallBackUPP,
- (long)drp,
- (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
- (long)drp->frameTime,
- (long)drp->scale);
- }
- else {
- // ran out of frames ... the queue is off, so mark it as such
- glob->queueIsRunning = false;
- }
- }
- }
-
- /************************************************************************************
- * CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal
- * band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we
- * demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and
- * we need to clear bits in it that correspond to any pixels in the destination we do not want to
- * change. ( We always write all pixels, so we dont care. This mode is important only for those
- * codecs that have frame differencing and don't always write all the pixels. )
- */
-
- pascal ComponentResult
- CDBandDecompress(Handle storage,register CodecDecompressParams *p)
- {
- OSErr result = noErr;
- Boolean callCompletionRoutine = true;
- Globals *glob = (Globals *)storage;
-
- if (p->frameTime) {
- /* this frame has a schedule time to be displayed. */
- DecompressRecord *drp;
- long i;
-
- // find an empty slot in the queue
- drp = glob->firstFreeEntry;
- if (!drp) { // we couldn't find an empty slot
- result = codecCantQueueErr;
- goto bail;
- }
-
- if (!glob->callBack) {
- // there is no current callback. allocate one.
- glob->callBack = NewCallBack(p->frameTime->base,callBackAtTime + callBackAtInterrupt + callBackAtDeferredTask);
- if (!glob->callBack) {
- result = codecCantQueueErr;
- goto bail;
- }
- glob->a5World = SetA5(0);
- SetA5(glob->a5World); // is there a way to get a5 without changing it?
- }
-
- glob->firstFreeEntry = drp->nextFree;
-
- drp->frameTime = p->frameTime->value.lo;
- drp->scale = p->frameTime->scale;
- drp->rate = p->frameTime->rate;
- drp->glob = (void *)glob;
- drp->inUse = true;
-
- Enqueue((void *)(&drp->next), &glob->head); // put the frame in the queue
- if (!glob->queueIsRunning) {
- // the queue isn't running... start it up
- glob->queueIsRunning = true; // since CallMeWhen could be considered "CallMeRightNow"
- if (result = CallMeWhen(
- glob->callBack,
- glob->decompressCallBackUPP,
- (long)drp,
- (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
- (long)drp->frameTime,
- (long)drp->scale)) {
- // there was an error
- glob->queueIsRunning = false; // if error, queue isn't running
- drp->inUse = false;
- Dequeue((void *)&drp->next, &glob->head); // pull it out of the queue
- drp->nextFree = glob->firstFreeEntry;
- glob->firstFreeEntry = drp;
- goto bail;
- }
- }
- callCompletionRoutine = false;
- }
-
- bail:
- if (callCompletionRoutine)
- ICMDecompressComplete(p->sequenceID, result, codecCompletionSource | codecCompletionDest, &p->completionProcRecord);
-
- return result;
- }
-
- /************************************************************************************
- * CDGetCodecInfo allows us to return information about ourselves to the codec manager.
- *
- * There will be a tool for determining appropriate values for the accuracy, speed
- * and level information. For now we estimate with scientific wild guessing.
- *
- * The info is stored as a resource in the same file with our component.
- */
-
- pascal ComponentResult
- CDGetCodecInfo(Handle storage,CodecInfo *info)
- {
- Globals *glob = (Globals *)storage;
-
- if ( info == nil )
- return paramErr;
-
- BlockMoveData("\praw faker", info->typeName, 32);
- info->version = 1;
- info->revisionLevel = 1;
- info->vendor = 'fake';
- info->decompressFlags = codecInfoDoes32;
- info->compressFlags = 0;
- info->formatFlags = codecInfoDepth16 | codecInfoDepth32;
- info->compressionAccuracy = 100;
- info->decompressionAccuracy = 100;
- info->compressionSpeed = 100;
- info->decompressionSpeed = 98;
- info->compressionLevel = 100;
- info->resvd = 0;
- info->minimumHeight = 1;
- info->minimumWidth = 1;
- info->decompressPipelineLatency = 0;
- info->compressPipelineLatency = 0;
- info->privateData = 0;
-
- return noErr;
- }
-
- pascal ComponentResult
- CDCodecNewImageBufferMemory(Handle storage, CodecDecompressParams *p, long flags, ICMMemoryDisposedUPP memoryGoneProc, void *refCon)
- {
- #pragma unused(memoryGoneProc, refCon)
-
- OSErr err = noErr;
- long offsetH, offsetV;
- Ptr baseAddr;
-
- // call predecompress to check to make sure we can handle this destination
- err = CDPreDecompress(storage, p);
- if (err) goto bail;
-
- // calculate a base address to write to
- offsetH = (p->dstRect.left - p->dstPixMap.bounds.left);
- switch (p->dstPixMap.pixelSize) {
- case 32: offsetH *= 4; break;
- case 16: offsetH *= 2; break;
- case 8: offsetH *= 1; break;
- default: return paramErr;
- }
-
- offsetV = (p->dstRect.top - p->dstPixMap.bounds.top) * (0x7fff & p->dstPixMap.rowBytes);
- baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
-
- // return the base address and row bytes to use in the dstPixMap structure
- p->dstPixMap.baseAddr = baseAddr;
- p->dstPixMap.rowBytes = p->dstPixMap.rowBytes;
- p->capabilities->flags = codecImageBufferIsOnScreen;
-
- bail:
- return err;
- }
-
- #ifdef LINK_EXAMPLE_CODEC
-
- void InstallRawFakerCodec(void);
- void InstallRawFakerCodec(void)
-
- {
- ComponentDescription td;
- Component c;
- Handle dname;
-
- dname = NewHandle(19);
- td.componentType = 'imdc';
- td.componentSubType = 'raw ';
- td.componentManufacturer = 'fake';
- td.componentFlags = codecInfoDoes32;
- td.componentFlagsMask = 0;
-
- BlockMoveData("\pTEST Raw Faker DECO",*dname,19);
- if ((c= RegisterComponent(&td,NewComponentRoutineProc(RAWFAKEREXAMPLECODEC), 0,dname,nil, nil)) == 0 ) {
- Debugger();
- }
- SetDefaultComponent(c,defaultComponentAnyManufacturer+defaultComponentAnyFlags);
- }
- #endif
-